/**
 * @file drv_soft_rtc.c
 * @brief Software implementation of software RTC for 51 microcontroller.
 * @author David Xu
 * @version 1.0.0
 * @date 2023-11-15
 * @note none
 */
#include "drv_soft_rtc.h"


// Global variables to store the current time and alarm time
static RTC_DateTime_t g_currentDateTime = { 0 };
static RTC_DateTime_t g_alarmDateTime   = { 0 };

// Flags to simulate interrupts
static bool g_timeTickInterruptEnabled = false;
static bool g_alarmInterruptEnabled    = false;

// Flag to check if the alarm time has been reached
static bool g_alarmTriggered = false;

// Global variable to indicate whether the RTC is running
static bool g_rtcRunning = false;

/**
 * @brief Starts the RTC module.
 *
 * This function enables the RTC to begin counting.
 */
void ct_soft_rtc_Run(void) {
    g_rtcRunning = true;
}

/**
 * @brief Stops the RTC module.
 *
 * This function pauses the RTC, halting its counting temporarily.
 */
void ct_soft_rtc_Pause(void) {
    g_rtcRunning = false;
}


/**
 * @brief Determines if a year is a leap year.
 *
 * @param year The year to check.
 * @return true if the year is a leap year, false otherwise.
 */
static bool isLeapYear(unsigned short year) {
    return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
}

/**
 * @brief Gets the number of days in a month.
 *
 * @param month The month to check.
 * @param year The year to check (important for February in leap years).
 * @return The number of days in the specified month.
 */
static unsigned char getDaysInMonth(unsigned char month, unsigned short year) {
    switch (month) {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:
            return 31;
        case 4:
        case 6:
        case 9:
        case 11:
            return 30;
        case 2:
            return isLeapYear(year) ? 29 : 28;
        default:
            return 0;    // Invalid month
    }
}

/**
 * @brief Simulates the passage of time, should be called regularly, e.g., once per second.
 */
void ct_soft_rtc_Tick(void) {
    // Do not update time if the RTC is not running
    if (!g_rtcRunning) {
        return;
    }
    // Increase seconds
    g_currentDateTime.time.second++;

    // Handle carry for minutes and hours
    if (g_currentDateTime.time.second >= 60) {
        g_currentDateTime.time.second = 0;
        g_currentDateTime.time.minute++;
        if (g_currentDateTime.time.minute >= 60) {
            g_currentDateTime.time.minute = 0;
            g_currentDateTime.time.hour++;
            if (g_currentDateTime.time.hour >= 24) {
                g_currentDateTime.time.hour = 0;
                // Handle carry for the date
                g_currentDateTime.date.day++;
                // Consider the specific number of days in a month and leap years
                if (g_currentDateTime.date.day > getDaysInMonth(g_currentDateTime.date.month, g_currentDateTime.date.year)) {
                    g_currentDateTime.date.day = 1;
                    g_currentDateTime.date.month++;
                    if (g_currentDateTime.date.month > 12) {
                        g_currentDateTime.date.month = 1;
                        g_currentDateTime.date.year++;
                    }
                }
            }
        }
    }

    // Check if the alarm time has been reached
    if (g_alarmInterruptEnabled && g_currentDateTime.date.year == g_alarmDateTime.date.year && g_currentDateTime.date.month == g_alarmDateTime.date.month && g_currentDateTime.date.day == g_alarmDateTime.date.day && g_currentDateTime.time.hour == g_alarmDateTime.time.hour && g_currentDateTime.time.minute == g_alarmDateTime.time.minute && g_currentDateTime.time.second == g_alarmDateTime.time.second) {
        g_alarmTriggered = true;
    }

    // If time tick interrupt is enabled
    if (g_timeTickInterruptEnabled) {
        // Time tick interrupt handler should be called here
    }

    // If the alarm is triggered
    if (g_alarmTriggered) {
        g_alarmTriggered = false;
        // Alarm interrupt handler should be called here
    }
}


/**
 * @brief Sets the current date.
 *
 * @param date Pointer to the RTC_Date_t structure containing the new date.
 */
void ct_soft_rtc_SetDate(const RTC_Date_t *date) {
    if (date != NULL) {
        g_currentDateTime.date = *date;
    }
}

/**
 * @brief Sets the RTC time.
 *
 * @param[in] time Pointer to a RTC_Time_t structure containing the new time to set.
 */
void ct_soft_rtc_SetTime(const RTC_Time_t *time) {
    if (time != NULL) {
        g_currentDateTime.time = *time;
    }
}

/**
 * @brief Retrieves the current date and time from the RTC.
 *
 * @param[out] dateTime Pointer to a RTC_DateTime_t structure to store the current date and time.
 * @note The function will convert the S_RTC_TIME_DATA_T structure to RTC_DateTime_t structure.
 */
void ct_soft_rtc_GetDateAndTime(RTC_DateTime_t *dateTime) {
    if (dateTime != NULL) {
        *dateTime = g_currentDateTime;
    }
}

/**
 * @brief Sets the RTC alarm date.
 *
 * @param[in] date Pointer to a RTC_Date_t structure containing the new alarm date to set.
 * @note The function will not return a value but may modify hardware registers.
 */
void ct_soft_rtc_SetAlarmDate(const RTC_Date_t *date) {
    if (date != NULL) {
        g_alarmDateTime.date = *date;
    }
}


/**
 * @brief Sets the RTC alarm time.
 *
 * @param[in] time Pointer to a RTC_Time_t structure containing the new alarm time to set.
 * @note The function will not return a value but may modify hardware registers.
 *    TimeMode:    The 12-Hour / 24-Hour Time Scale Selection. [ \ref RTC_CLOCK_12 / \ref RTC_CLOCK_24 ]
 *     AmPm:        The AM / PM of 12-Hour Time Scale Selection. [ \ref RTC_AM / \ref RTC_PM ]
 */
void ct_soft_rtc_SetAlarmTime(const RTC_Time_t *time) {
    if (time != NULL) {
        g_alarmDateTime.time = *time;
    }
}

/**
 * @brief Retrieves the current alarm date and time from the RTC.
 *
 * @param[out] dateTime Pointer to a RTC_DateTime_t structure to store the current alarm date and time.
 * @note The function will convert the S_RTC_TIME_DATA_T structure to RTC_DateTime_t structure.
 */
void ct_soft_rtc_GetAlarmDateAndTime(RTC_DateTime_t *dateTime) {
    if (dateTime != NULL) {
        *dateTime = g_alarmDateTime;
    }
}

/**
 * @brief Enables a specified RTC interrupt.
 *
 * @param[in] intType The type of interrupt to enable. This can be one of the following:
 *                    - \ref CT_RTC_TIME_TICK for the time tick interrupt.
 *                    - \ref CT_RTC_ALARM for the alarm interrupt.
 * @note The function will not return a value but may modify hardware registers.
 */
void ct_soft_rtc_EnableInterrupt(uint8_t intType) {
    if (intType == CT_RTC_TIME_TICK) {
        g_timeTickInterruptEnabled = true;
    } else if (intType == CT_RTC_ALARM) {
        g_alarmInterruptEnabled = true;
    }
}

/**
 * @brief Disables a specified RTC interrupt.
 *
 * @param[in] intType The type of interrupt to disable. This can be one of the following:
 *                    - \ref CT_RTC_TIME_TICK for the time tick interrupt.
 *                    - \ref CT_RTC_ALARM for the alarm interrupt.
 * @note The function will not return a value but may modify hardware registers.
 */
void ct_soft_rtc_DisableInterrupt(uint8_t intType) {
    if (intType == CT_RTC_TIME_TICK) {
        g_timeTickInterruptEnabled = false;
    } else if (intType == CT_RTC_ALARM) {
        g_alarmInterruptEnabled = false;
    }
}

/**
 * @brief Initializes the RTC module.
 */
void ct_soft_rtc_Init(void) {
    // 初始化当前日期和时间
    RTC_Date_t currentDate = { 2023, 1, 1, CT_RTC_SUNDAY };
    RTC_Time_t currentTime = { 0, 0, 0, CT_RTC_CLOCK_24, CT_RTC_AM };

    // 设置日期和时间
    ct_soft_rtc_SetDate(&currentDate);
    ct_soft_rtc_SetTime(&currentTime);

    // 启用时间滴答中断
    ct_soft_rtc_EnableInterrupt(CT_RTC_TIME_TICK);
}

/**
 * @brief Test function for the RTC.
 */
void ct_soft_rtc_test(void) {
    // 设置当前日期和时间
    RTC_Date_t currentDate = { 2023, 4, 15, CT_RTC_SUNDAY };
    RTC_Time_t currentTime = { 12, 30, 0, CT_RTC_CLOCK_24, CT_RTC_AM };

    // 初始化RTC
    ct_soft_rtc_Init();



    ct_soft_rtc_SetDate(&currentDate);
    ct_soft_rtc_SetTime(&currentTime);

    // 启动RTC
    ct_soft_rtc_Run();

    // 在主循环中，定期调用ct_soft_rtc_Tick来模拟时间流逝
    while (true) {
        ct_soft_rtc_Tick();
        // 延时一秒（这里需要实现一个延时函数）
        // delay(1000);
    }
}